using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;

namespace Fusion.Net.TCP
{
    public class TCPServer : TCPNet
    {
        public event Action<SocketBase> OnConnected;

        public void InvokeOnConnected(SocketBase socket)
        {
            OnConnected?.Invoke(socket);
        }

        public void OnHeartBeatedFromClient(SocketBase socket)
        {
            TCPClientSocket client = socket as TCPClientSocket;
            client.SendHeartBeatsResponse();
        }

        List<TCPClientSocket> clients = null;
        private object clientsHandleLock = new object();

        private float heartBeatsCheckInterval = -1;

        public List<TCPClientSocket> Clients { get => clients; }

        public TCPServer(int port) : base()
        {
            clients = new List<TCPClientSocket>();
            if (NetUtility.OSSupportsIPv6())
            {
                CreateSocket(port, AddressFamily.InterNetworkV6);
            }
            else if (NetUtility.OSSupportsIPv4())
            {
                CreateSocket(port, AddressFamily.InterNetwork);
            }
        }

        public TCPServer(int port, AddressFamily addressFamily) : base()
        {
            clients = new List<TCPClientSocket>();
            CreateSocket(port, addressFamily);
        }

        private void CreateSocket(int port, AddressFamily addressFamily)
        {
            if (addressFamily == AddressFamily.InterNetworkV6)
            {
                socket = new TCPServerSocket(this, AddressFamily.InterNetworkV6);
                socket.Bind(IPAddress.IPv6Any, port);
            }
            else if (addressFamily == AddressFamily.InterNetwork)
            {
                socket = new TCPServerSocket(this, AddressFamily.InterNetwork);
                socket.Bind(IPAddress.Any, port);
            }
            OnHeartBeated += OnHeartBeatedFromClient;
        }

        public override void Close(int code = 0)
        {
            base.Close(code);

            lock (clientsHandleLock)
            {
                for (int clientIndex = 0; clientIndex < clients.Count; clientIndex++)
                {
                    clients[clientIndex].Close();
                }
                clients.Clear();
            }
        }

        public void AddClient(TCPClientSocket tcpClientSocketData)
        {
            lock (clientsHandleLock)
            {
                tcpClientSocketData.SetHeartBeatsChecker(heartBeatsCheckInterval);
                clients.Add(tcpClientSocketData);
            }
        }

        public void RemoveClient(TCPClientSocket tcpClientSocketData)
        {
            lock (clientsHandleLock)
            {
                clients.Remove(tcpClientSocketData);
            }
        }

        public void SendBroadcast(byte[] data, int length = -1)
        {
            lock (clientsHandleLock)
            {
                for (int index = 0; index < clients.Count; index++)
                {
                    clients[index].Send(data, length > 0 ? length : data.Length);
                }
            }
        }

        public void SetHeartBeatsChecker(float interval)
        {
            heartBeatsCheckInterval = interval;
            lock (clientsHandleLock)
            {
                for (int index = 0; index < clients.Count; index++)
                {
                    clients[index].SetHeartBeatsChecker(interval);
                }
            }
        }
    }
}


